package com.hippo.core.utils;

import cn.hutool.core.util.IdUtil;

import javax.annotation.processing.FilerException;
import javax.crypto.Cipher;
import javax.crypto.CipherInputStream;
import javax.crypto.CipherOutputStream;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.DESKeySpec;
import javax.crypto.spec.IvParameterSpec;
import java.io.*;
import java.security.Key;
import java.security.SecureRandom;
import java.util.Base64;



/**
 * @ClassName DESUtil
 * @Description TODO des加密工具类
 * @Author tangxl
 * @create 2023-04-18 10:22
 **/
public class DESUtil {
    /**
     * 偏移变量，固定占8位字节
     */
    private final static String IV_PARAMETER = "12345678";
    /**
     * 加密/解密算法-工作模式-填充模式
     */
    private static final String CIPHER_ALGORITHM = "DES/CBC/PKCS5Padding";
    /**
     * 默认编码
     */
    private static final String CHARSET = "utf-8";
    /**
     * 算法
     */
    private static final String ALGORITHM = "DES";
    /**
     * 生成key
     *
     * @param password
     * @return
     * @throws Exception
     */
    private static Key generateKey(String password) throws Exception {
        // 实例化DES密钥材料
        DESKeySpec dks = new DESKeySpec(password.getBytes(CHARSET));
        // 实例化秘密密钥工厂
        SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(ALGORITHM);
        // 生成秘密密钥
        return keyFactory.generateSecret(dks);
    }
    /**
     * DES加密字符串
     *
     * @param password 加密密码，长度不能够小于8位
     * @param data 待加密字符串
     * @return 加密后内容
     */
    public static String encrypt(String password, String data) {
        if (password== null || password.length() < 8) {
            throw new RuntimeException("加密失败，key不能小于8位");
        }
        if (data == null) {
            return null;
        }
        try {
            // 生成秘钥key
            Key secretKey = generateKey(password);
            // 实例化Cipher对象，它用于完成实际的加密操作
            Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
            // 初始化Cipher对象，设置为加密模式(二选一)
//            SecureRandom random = new SecureRandom();
            IvParameterSpec iv = new IvParameterSpec(IV_PARAMETER.getBytes(CHARSET));
            // 用密匙初始化Cipher对象,ENCRYPT_MODE用于将 Cipher 初始化为加密模式的常量
            cipher.init(Cipher.ENCRYPT_MODE, secretKey, iv);
            // 执行加密操作，加密后的结果通常都会用Base64编码进行传输
            byte[] bytes = cipher.doFinal(data.getBytes(CHARSET));

            //JDK1.8及以上可直接使用Base64，JDK1.7及以下可以使用BASE64Encoder
            //Android平台可以使用android.util.Base64
            return new String(Base64.getEncoder().encode(bytes));

        } catch (Exception e) {
            e.printStackTrace();
            return data;
        }
    }

    /**
     * DES解密字符串
     *
     * @param password 解密密码，长度不能够小于8位
     * @param data 待解密字符串
     * @return 解密后内容
     */
    public static String decrypt(String password, String data) {
        if (password== null || password.length() < 8) {
            throw new RuntimeException("加密失败，key不能小于8位");
        }
        if (data == null) {
            return null;
        }
        try {
            // 生成秘钥key
            Key secretKey = generateKey(password);
            // 实例化Cipher对象，它用于完成实际的解密操作
            Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
            // 初始化Cipher对象，设置为加密模式(二选一)
//            SecureRandom random = new SecureRandom();
            IvParameterSpec iv = new IvParameterSpec(IV_PARAMETER.getBytes(CHARSET));
            // 用密匙初始化Cipher对象,DECRYPT_MODE用于将 Cipher 初始化为解密模式的常量
            cipher.init(Cipher.DECRYPT_MODE, secretKey, iv);
            // 执行解密操作
            return new String(cipher.doFinal(Base64.getDecoder().decode(data.getBytes(CHARSET))), CHARSET);
        } catch (Exception e) {
            e.printStackTrace();
            return data;
        }
    }
    /**
     * DES加密文件
     *
     * @param srcFile  待加密的文件
     * @param destFile 加密后存放的文件路径
     * @return 加密后的文件路径
     */
    public static String encryptFile(String password, String srcFile, String destFile) {

        if (password== null || password.length() < 8) {
            throw new RuntimeException("加密失败，key不能小于8位");
        }
        try {
            // 初始化Cipher对象，设置为加密模式(二选一)
//            SecureRandom random = new SecureRandom();
            IvParameterSpec iv = new IvParameterSpec(IV_PARAMETER.getBytes(CHARSET));
            // 实例化Cipher对象，它用于完成实际的解密操作
            Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
            // 用密匙初始化Cipher对象,ENCRYPT_MODE用于将 Cipher 初始化为加密模式的常量
            cipher.init(Cipher.ENCRYPT_MODE, generateKey(password), iv);
            // 读取文件执行加密操作
            InputStream is = new FileInputStream(srcFile);
            OutputStream out = new FileOutputStream(destFile);
            CipherInputStream cis = new CipherInputStream(is, cipher);
            byte[] buffer = new byte[1024];
            int r;
            while ((r = cis.read(buffer)) > 0) {
                out.write(buffer, 0, r);
            }
            cis.close();
            is.close();
            out.close();
            return destFile;
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        return null;
    }
    /**
     * DES加密文件流
     *
     * @param password 加密密码，长度不能够小于8位
     * @param is 待加密的文件流
     * @param os 加密后存放的文件流
     * @return 加密后的文件流
     */
    public static void encryptFile(String password, InputStream is, OutputStream os) {

        if (password== null || password.length() < 8) {
            throw new RuntimeException("加密失败，key不能小于8位");
        }
        try {
            // 初始化Cipher对象，设置为加密模式(二选一)
//            SecureRandom random = new SecureRandom();
            IvParameterSpec iv = new IvParameterSpec(IV_PARAMETER.getBytes(CHARSET));
            // 实例化Cipher对象，它用于完成实际的解密操作
            Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
            // 用密匙初始化Cipher对象,ENCRYPT_MODE用于将 Cipher 初始化为加密模式的常量
            cipher.init(Cipher.ENCRYPT_MODE, generateKey(password), iv);
            // 执行加密操作
            CipherInputStream cis = new CipherInputStream(is, cipher);
            byte[] buffer = new byte[1024];
            int r;
            while ((r = cis.read(buffer)) > 0) {
                os.write(buffer, 0, r);
            }
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }

    /**
     * DES解密文件
     *
     * @param srcFile  已加密的文件
     * @param destFile 解密后存放的文件路径
     * @return 解密后的文件路径
     */
    public static String decryptFile(String password, String srcFile, String destFile) {
        if (password== null || password.length() < 8) {
            throw new RuntimeException("加密失败，key不能小于8位");
        }
        InputStream is = null;
        OutputStream out = null;
        CipherOutputStream cos = null;
        try{
            File file = new File(destFile);
            if (!file.exists()) {
                file.getParentFile().mkdirs();
                file.createNewFile();
            }
            IvParameterSpec iv = new IvParameterSpec(IV_PARAMETER.getBytes(CHARSET));
            Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
            cipher.init(Cipher.DECRYPT_MODE, generateKey(password), iv);
            is = new FileInputStream(srcFile);
            out = new FileOutputStream(destFile);
            cos = new CipherOutputStream(out, cipher);
            byte[] buffer = new byte[1024];
            int r;
            while ((r = is.read(buffer)) >= 0) {
                cos.write(buffer, 0, r);
            }
            return destFile;
        } catch (Exception ex) {
            ex.printStackTrace();
        }finally {
            if (cos!=null) {
                try {
                    cos.close();
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }
            if (is!=null) {
                try {
                    is.close();
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }
            if (out!=null) {
                try {
                    out.close();
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }
        }
        return null;
    }

    /**
     * DES解密文件流
     *
     * @param password 密码
     * @param is 已加密的文件流
     * @param out 解密后存放的文件流
     * @return 解密后的文件流
     */
    public static void decryptFile(String password, InputStream is, OutputStream out) {
        if (password== null || password.length() < 8) {
            throw new RuntimeException("加密失败，key不能小于8位");
        }
        CipherOutputStream cos = null;
        try {
            IvParameterSpec iv = new IvParameterSpec(IV_PARAMETER.getBytes(CHARSET));
            Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
            cipher.init(Cipher.DECRYPT_MODE, generateKey(password), iv);
            cos = new CipherOutputStream(out, cipher);
            byte[] buffer = new byte[1024];
            int r;
            while ((r = is.read(buffer)) >= 0) {
                cos.write(buffer, 0, r);
            }
        } catch (Exception ex) {
            ex.printStackTrace();
        }finally {
            if(cos != null){
                try {
                    cos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    public static void main(String[] args) {
        /**
         * 加密解密字符串
         */
        // hutool工具类生成随机字符串当加密秘钥
        String password = IdUtil.randomUUID();
//        System.out.println("加密秘钥：" + password);
//        // 加密字符串
//        String strData = "加密字符串";
//        // 字符串des加密
//        String encrypt = encrypt(password, strData);
//        System.out.println("加密后：" + encrypt);
//        // 字符串des解密
//        String decrypt = decrypt(password, encrypt);
//        System.out.println("解密后：" + decrypt);
        /**
         * 加密解密文件
         */
//        // 加密文件
//        String srcFile = "study-commons/core/src/main/resources/file/test.txt";
//        // 加密后文件
//        String destFile = "study-commons/core/src/main/resources/file/encryptFile/test.txt";
//        String encryptFile = encryptFile(password, srcFile, destFile);
//        System.out.println("加密后文件地址：" + encryptFile);
//        // 解密文件
//        String decryptFile = decryptFile(password, encryptFile, "study-commons/core/src/main/resources/file/decryptFile/test.txt");
//        System.out.println("解密后文件地址：" + decryptFile);
        /**
         * 加密解密文件流
         */
        String srcPath = "study-commons/core/src/main/resources/file/data.dat";
        String encryptPath = "study-commons/core/src/main/resources/file/encryptFile/data.dat";
        String decryptPath = "study-commons/core/src/main/resources/file/decryptFile/data.dat";
//        // 文件加密
//        try(
//                InputStream is = new FileInputStream(srcPath);
//                OutputStream os = new FileOutputStream(encryptPath);
//                ) {
//            // 文件流加密
//            encryptFile(password, is,os);
//            System.out.println("加密后文件地址：" + encryptPath);
//        } catch (Exception e) {
//            throw new RuntimeException(e);
//        }
        // 文件流解密
//        try(
//                InputStream in = new FileInputStream(encryptPath);
//                OutputStream out = new FileOutputStream(decryptPath);
//                ){
//            decryptFile(password, in, out);
//            System.out.println("解密后文件地址：" + decryptPath);
//        }catch (Exception e) {
//            e.printStackTrace();
//        }


    }

}
